package bsplib.q3bsp;

import bsplib.Vec3f;
import bsplib.BspFace;
import bsplib.BspNode;
import bsplib.BspPlane;
import bsplib.BspTriangle;
import bsplib.BspVertex;
import flash.display.BitmapData;
import org.flashdevelop.utils.FlashConnect;

import flash.Vector;

import flash.utils.ByteArray;

class Q3Bsp
{
    
    public var entities    : Vector<Int>;
    public var textures    : Vector<Q3Texture>;
    public var planes      : Vector<Q3Plane>;
    public var nodes       : Vector<Q3Node>;
    public var leafs       : Vector<Q3Leaf>;
    public var leafFaces   : Vector<Int>;
    public var leafBrushes : Vector<Int>;
    public var models      : Vector<Int>;
    public var brushes     : Vector<Q3Brush>;
    public var brushSides  : Vector<Q3BrushSide>;
    public var vertexes    : Vector<Q3Vertex>;
    public var meshVerts   : Vector<Int>;
    public var effects     : Vector<Int>;
    public var faces       : Vector<Q3Face>;
    public var lightMaps   : Vector<Q3LightMap>;
    public var lightVols   : Vector<Int>;
    public var visdata     : Q3VisData;
    
    private static inline var DIR_ENTITIES     : UInt = 0;
    private static inline var DIR_TEXTURES     : UInt = 1;
    private static inline var DIR_PLANES       : UInt = 2;
    private static inline var DIR_NODES        : UInt = 3;
    private static inline var DIR_LEAFS        : UInt = 4;
    private static inline var DIR_LEAF_FACES   : UInt = 5;
    private static inline var DIR_LEAF_BRUSHES : UInt = 6;
    private static inline var DIR_MODELS       : UInt = 7;
    private static inline var DIR_BRUSHES      : UInt = 8;
    private static inline var DIR_BRUSH_SIDES  : UInt = 9;
    private static inline var DIR_VERTEXES     : UInt = 10;
    private static inline var DIR_MESH_VERTS   : UInt = 11;
    private static inline var DIR_EFFECTS      : UInt = 12;
    private static inline var DIR_FACES        : UInt = 13;
    private static inline var DIR_LIGHTMAPS    : UInt = 14;
    private static inline var DIR_LIGHTVOLS    : UInt = 15;
    private static inline var DIR_VISDATA      : UInt = 16;
    private static inline var DIR_MAX          : UInt = 17;
    
    public function new( data : ByteArray )
    {
        entities    = new Vector<Int>();
        textures    = new Vector<Q3Texture>();
        planes      = new Vector<Q3Plane>();
        nodes       = new Vector<Q3Node>();
        leafs       = new Vector<Q3Leaf>();
        leafFaces   = new Vector<Int>();
        leafBrushes = new Vector<Int>();
        models      = new Vector<Int>();
        brushes     = new Vector<Q3Brush>();
        brushSides  = new Vector<Q3BrushSide>();
        vertexes    = new Vector<Q3Vertex>();
        meshVerts   = new Vector<Int>();
        effects     = new Vector<Int>();
        faces       = new Vector<Q3Face>();
        lightMaps   = new Vector<Q3LightMap>();
        lightVols   = new Vector<Int>();
        
        
        // Header:
        
        // TODO : Compare with IBSP
        var magicString = data.readUTFBytes(4);
        
        var version = data.readInt();
        
        var dirOffsets = new Vector<Int>(17);
        var dirLengths = new Vector<Int>(17);
        

        for ( dirIdx in 0...DIR_MAX ) {
            dirOffsets[dirIdx] = data.readInt();
            dirLengths[dirIdx] = data.readInt();
        }
        
        var readList = function( dataClass : Dynamic, list : Dynamic ) : Void {
            var dirIndex : UInt = dataClass.DIR_INDEX;
            data.position = dirOffsets[dirIndex];
            var limit = dirOffsets[dirIndex] + dirLengths[dirIndex];
            while( data.position < limit )
                list.push( Type.createInstance(dataClass, [data]) );
        }
        
        var readIntList = function( dirIndex : UInt, list : Vector<Int> ) : Void {
            data.position = dirOffsets[dirIndex];
            var limit : UInt = dirOffsets[dirIndex] + dirLengths[dirIndex];
            while( data.position < limit )
                list.push( data.readInt() );
        }
        
        // TODO Read entities:
        readList( Q3Texture, textures );
        readList( Q3Plane, planes );
        readList( Q3Node, nodes );
        readList( Q3Leaf, leafs );
        readIntList( DIR_LEAF_FACES, leafFaces );
        readIntList( DIR_LEAF_BRUSHES, leafBrushes );
        // TODO Read models
        readList( Q3Brush, brushes );
        readList( Q3BrushSide, brushSides );
        readList( Q3Vertex, vertexes );
        readIntList( DIR_MESH_VERTS, meshVerts );
        // TODO Read effects
        readList( Q3Face, faces );
        readList( Q3LightMap, lightMaps );
        // TODO Read lightvols
        data.position = dirOffsets[Q3VisData.DIR_INDEX];
        visdata = new Q3VisData( data );
    }
    
    public function process( texturesHash : Hash<BitmapData> ) : BspNode {
        var bspPlanes = new Vector<BspPlane>( planes.length );
        var bspLeafs  = new Vector<BspNode>( leafs.length );
        var bspNodes  = new Vector<BspNode>( nodes.length );
        var bspFaces  = new Vector<BspFace>( faces.length );
        
        //var i : Int;
        for ( i in 0...planes.length ) {
            var plane = planes[i]; 
            bspPlanes[i] = new BspPlane( plane.normal.toVec3f(), plane.dist );
        }
        
        var defaultTex = new BitmapData(10, 10,false);
        
        for ( i in 0...faces.length ) {
            var bspFace = new BspFace();
            var face = faces[i];
            bspFace.texture = defaultTex;
            if ( face.type == 1 ) {
                var texture = texturesHash.get( textures[ face.texture ].name + ".jpg" );
                bspFace.texture = if ( texture != null ) texture else defaultTex;
                
                if ( face.lmIndex >= 0 ) {
                    bspFace.lightmap = lightMaps[ face.lmIndex ].ldata;
                }
                
                bspFace.numTris     = Std.int( face.nMeshVerts / 3 );
                bspFace.verts       = new Vector<BspVertex>(face.nVertexes);
                bspFace.tris        = new Vector<BspTriangle>(bspFace.numTris);

                for ( j in 0...face.nVertexes ) {
                    var vertex : Q3Vertex = vertexes[ face.vertex + j ];
                    
                    bspFace.verts[j] = new BspVertex( j, vertex.pos.toVec3f(), vertex.texUV.toVec2f(), vertex.lmUV.toVec2f() );
                }
                
                var meshVertIndex = 0;
                for ( j in 0...bspFace.numTris ) {
                    var bspTri = new BspTriangle();
                    bspTri.v0 = bspFace.verts[ meshVerts[face.meshVert + meshVertIndex++] ];
                    bspTri.v1 = bspFace.verts[ meshVerts[face.meshVert + meshVertIndex++] ];
                    bspTri.v2 = bspFace.verts[ meshVerts[face.meshVert + meshVertIndex++] ];
                    bspFace.tris[j] = bspTri;
                }

                bspFace.normal = face.normal.toVec3f();
            }
            bspFaces[i] = bspFace;
        }
        
        for ( i in 0...bspNodes.length ) {
            var bspNode:BspNode = new BspNode();
            var node : Q3Node = nodes[i];
            bspNode.plane = bspPlanes[node.plane];
            bspNodes[i] = bspNode;
        }
        
        for ( i in 0...bspLeafs.length ) {
           var bspNode = new BspNode();
            var leaf : Q3Leaf = leafs[i];
            bspNode.faces = new Vector<BspFace>( leaf.nLeafFaces );
            
            for ( j in 0...leaf.nLeafFaces ) {
                bspNode.faces[j] = bspFaces[ leafFaces[leaf.leafFace + j] ].shallowClone();
            }
            
            bspNode.cluster = leaf.cluster;
            
            bspLeafs[i] = bspNode;
        }
        
        for ( i in 0...bspNodes.length) {
            var bspNode = bspNodes[i];
            var node = nodes[i];
            bspNode.child0 = (node.child0 >= 0)? bspNodes[node.child0] : bspLeafs[-node.child0 -1];
            bspNode.child1 = (node.child1 >= 0)? bspNodes[node.child1] : bspLeafs[-node.child1 -1];
        }
        
        return bspNodes[0];
    }

}
